// CSE571 - player-game.cpp
// Authors: Jonathan Wald, Daniel Sarfati, Guangle Fan
// This class define methods for each game that a client-side player connects to.

#include <sstream>
#include <algorithm>

#include "player-game.h"
#include "card.h"
#include "poker.h"
#include "hand.h"
#include "fivecarddraw.h"
#include "game-manager.h"
#include "score-cal.h"
#include "globals.h"
#include "io-manager.h"

const unsigned int NUM_CARDS = 5;

struct send_and_print
  : std::unary_function<Card, void> {
  send_and_print (PlayerGame* game) {
    game_ = game;
  }

  void operator() (Card card) {
    game_->handler.enqueue_card_for_output(printSuit(card)[0],
                                    printRank(card)[0]);
    game_->stream << card << " ";
  }
  PlayerGame* game_;
};


PlayerGame::PlayerGame() : finalHand(false) {
  failed =  false;
  terminated = false;
  suspended = false;
  handler.counterpart_quit = false;
}

PlayerGame::~PlayerGame() {
  ACE_Reactor::instance()->remove_handler(&handler, 
					  ACE_Event_Handler::READ_MASK | 
					  ACE_Event_Handler::WRITE_MASK);
  handler.shutdown();
}

void PlayerGame::send_msg(string message) {
    handler.enqueue_string_for_output(message);
}

void PlayerGame::send_card(Card card) {
  handler.enqueue_card_for_output(printSuit(card)[0],
				  printRank(card)[0]);
}

void PlayerGame::discard() {
  stream << "Discarding cards: ";
  FiveCardDraw draw;
  draw.auto_discard(this);
  for_each(draw.discardDeck.deckVec.begin(),
	   draw.discardDeck.deckVec.end(),
           send_and_print (this));
  if (draw.discardDeck.size() == 0) {
    stream << "None" << std::endl;
  }
  std::cout << "Done discarding." << std::endl;
  handler.enqueue_string_for_output("");
}

void PlayerGame::sendBackFinal() {
  //this method is used for sending final hand of game holdem back to dealer.
  vector<Card> handVec = hand.getHand();
  for_each(handVec.begin(), handVec.end(), send_and_print (this));
  stream.str("");
}

void PlayerGame::getPoolCard() {
  //this method will update current hand (two cards) with table cards in the way that leads to max score. 5 out of 7.
  if (DEBUG) cout << "All possible combinations:" << endl;
  vector<Card> temp = cardPool.deckVec;
  sort (temp.begin(), temp.end());
  vector<Card>::iterator begin = temp.begin(), end = temp.end();
  int mscore = -1;
  do {
    vector<Card>::iterator iter = begin;
    Hand ihand;
    while (iter != begin + NUM_CARDS) {
      if (DEBUG) cout << *iter << " ";
      ihand.add_card (*iter);
      ++iter;
    }
    int iscore = getHandScore (ihand);
    if (DEBUG) cout << "Score: " << iscore << endl;
    if (iscore > mscore) {
      hand = ihand;
      mscore = iscore;
    }
  } while (next_combination(begin, begin + NUM_CARDS, end));
}

void PlayerGame::reset() {
  dealer_name.clear();
  hand.emptyHand();
  finalHand = false;
  score = -1;
  //empty card pool for holdem
  cardPool.emptyDeck();

  // Once again, send dealer name.
  handler.enqueue_string_for_output(GameManager::instance()->get_name()); 
}

// Service methods
int PlayerGame::init (int, char *[]) {
  
  return 0;
}

int PlayerGame::fini (void) {
  return 0;
}

int PlayerGame::suspend (void) {
  // Suspend in-band
  IOManager::instance()->enqueue_suspend(&handler);
  return 0;
}

int PlayerGame::resume (void) {
  // Resume in-band
  IOManager::instance()->enqueue_resume(&handler);
  return 0;
}

int PlayerGame::info (char **, size_t) {
  std::cout << "\tService Info - Label: " << label << " Name: " << game_name << " Status: "
	    << (suspended ? std::string("Suspended") : std::string("Running"))
	    << std::endl;
  return 0;
}


// this template function will return next combination with size distance(first, k) from a sorted container. first and last mark the range within the container you want get combination from. k marks some middle point between them.
template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
  /* Credits: Mark Nelson http://marknelson.us */
  if ((first == last) || (first == k) || (last == k))
    return false;
  Iterator i1 = first;
  Iterator i2 = last;
  ++i1;
  if (last == i1)
    return false;
  i1 = k;
  --i2;
  while (first != i1)
    {
      if (*--i1 < *i2)
	{
	  Iterator j = k;
	  while (!(*i1 < *j)) ++j;
	  std::iter_swap(i1,j);
	  ++i1;
	  ++j;
	  i2 = k;
	  std::rotate(i1,j,last);
	  while (last != j)
	    {
	      ++j;
	      ++i2;
	    }
	  std::rotate(k,i2,last);
	  return true;
	}
    }
  std::rotate(first,k,last);
  return false;
}
